﻿#include "FITKTriMeshGenEarCut.h"
#include "FITK_Interface/FITKInterfaceModel/FITKElementFactory.h"
#include "FITK_Interface/FITKInterfaceModel/FITKElementTri.h"
#include "FITK_Kernel/FITKCore/FITKVec3D.h"
#include "FITK_Kernel/FITKCore/FITKTriangleAlg.h"
#include "FITK_Kernel/FITKCore/FITKVec3DAlg.h"

#include <vtkPoints.h>
#include <vtkUnstructuredGrid.h>
#include <vtkTriangleFilter.h>
#include <vtkPolygon.h>
#include <vtkCellArray.h>
#include <vtkCell.h>
#include <vtkSmartPointer.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <cmath>

namespace Interface
{
    void FITKTriMeshGenEarCut::setPointList(const QList<Core::FITKNode*>& nodeList)
    {
        _nodeList = nodeList;
    }

    bool Interface::FITKTriMeshGenEarCut::generateMesh()
    {
        vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
        vtkSmartPointer<vtkPolygon> polygon = vtkSmartPointer<vtkPolygon>::New();
        for (Core::FITKNode* node : _nodeList)
        {
            if (!node)continue;
            points->InsertNextPoint(node->x(), node->y(), node->z());
            polygon->GetPoints()->InsertNextPoint(node->x(), node->y(), node->z());
        }
        for (int i = 0; i < points->GetNumberOfPoints(); i++)
        {
            polygon->GetPointIds()->InsertNextId(i);
        }

        vtkSmartPointer<vtkIdList> tris = vtkSmartPointer<vtkIdList>::New();
        polygon->Triangulate(tris);

        for (int i = 0; i < tris->GetNumberOfIds() / 3; i++)
        {
            vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
            FITKAbstractElement* element = FITKElementFactory::createElement(FITKModelEnum::FITKEleType::Tri3);
            int indexP1 = tris->GetId(i * 3);
            int indexP2 = tris->GetId(i * 3 + 1);
            int indexP3 = tris->GetId(i * 3 + 2);
            
            Core::FITKNode* p1 = _nodeList.at(indexP1);
            Core::FITKNode* p2 = _nodeList.at(indexP2);
            Core::FITKNode* p3 = _nodeList.at(indexP3);

            element->setNodeID(QList<int>() << p1->getNodeID() << p2->getNodeID() << p3->getNodeID());
            _triElement.append(element);
        }
        return true;
    }

    void FITKTriMeshGenEarCut::startGenTriPatch()
    {
        if (_nodeList.size() < 3)return;
        int count = _nodeList.size();
        //根据割耳法则获取三角面片
        for (int i = 0; i < _nodeList.size(); ++i)
        {
            if (_nodeList.size() < 3) return;
            //获取一点
            Core::FITKNode* p2 = _nodeList.at(i);
            //获取上一个点与下一个点，构成三角形
            Core::FITKNode* p1 = nullptr;
            Core::FITKNode* p3 = nullptr;
            if (i == 0)
                p1 = _nodeList.last();
            else
                p1 = _nodeList.at(i - 1);
            if (i + 1 == _nodeList.size())
                p3 = _nodeList.first();
            else
                p3 = _nodeList.at(i + 1);
            if (!p1 || !p2 || !p3)continue;
            //根据获取的三角形顶点来判断该三角形在多边形里是否是耳朵
            //获取该点在多边形里的凸性
            if (!isPointConcaveConvex(p1, p2, p3))continue;
            //获取该三角形内部是否存在其他点
            if (isContainsOtherPoint(p1, p2, p3))continue;
            //生成三角面片
            FITKAbstractElement* element = FITKElementFactory::createElement(FITKModelEnum::FITKEleType::Tri3);
            element->setNodeID(QList<int>() << p1->getNodeID() << p2->getNodeID() << p3->getNodeID());
            _triElement.append(element);
            _nodeList.removeAt(i);
            --i;
        }
        if (count == _nodeList.size()) return;
        //递归
        startGenTriPatch();
    }

    void FITKTriMeshGenEarCut::calPolygonNormal()
    {
        if (_nodeList.size() < 3)return;
        QList<Core::FITKNode*>::iterator iter = _nodeList.begin();
        Core::FITKNode* p1 = *iter;
        int count = 0;
        //组成n - 2个三角形，通过三角形面积累加获取大致多边形方向
        for (++iter; iter + 1 != _nodeList.end(); ++iter)
        {
            Core::FITKNode* p2 = *iter;
            Core::FITKNode* p3 = *(iter + 1);
            //获取面积和法向量
            Core::FITKVec3D v12 = Subtract(*p2, *p1);
            Core::FITKVec3D v13 = Subtract(*p3, *p1);
            double area = Core::GetTriangleArea(*p1, *p2, *p3);
            if (fabs(area) < 1e-16)continue;
            Core::FITKVec3D normal = Core::CrossProduct(v12, v13);
            normal.normalize();
            //this->unitVector(normal);
            _polygonNormal[0] += (area * normal.x());
            _polygonNormal[1] += (area * normal.y());
            _polygonNormal[2] += (area * normal.z());
            ++count;
        }
        //利用加权平均获取多边形法向
        _polygonNormal[0] += _polygonNormal[0] / count;
        _polygonNormal[1] += _polygonNormal[1] / count;
        _polygonNormal[2] += _polygonNormal[2] / count;
    }
 

    bool FITKTriMeshGenEarCut::isPointConcaveConvex(Core::FITKNode* p1, Core::FITKNode* p2, Core::FITKNode* p3)
    {
        //获取法向量
        Core::FITKVec3D v12 = Subtract(*p2, *p1);
        Core::FITKVec3D v13 = Subtract(*p3, *p1);
        Core::FITKVec3D normal = Core::CrossProduct(v12, v13);
        //获取夹角，夹角小于90度为凸点，夹角大于90度为凹点
        if (normal.getNorm() < 1e-14) return false;
        double d = Core::AngleBetweenVec(normal, _polygonNormal);
        return d < FITK_PI / 2;
    }

    bool FITKTriMeshGenEarCut::isContainsOtherPoint(Core::FITKNode* p1, Core::FITKNode* p2, Core::FITKNode* p3)
    {
        for (QList<Core::FITKNode*>::iterator iter = _nodeList.begin(); iter != _nodeList.end(); ++iter)
        {
            if (*iter == p1 || *iter == p2 || *iter == p3)continue;
            if (Core::IsPointInsideTriangle(*p1, *p2, *p3, **iter))
                return true;
        }
        return false;
    }


    QList<FITKAbstractElement*> FITKTriMeshGenEarCut::getTriangle() const
    {
        return _triElement;
    }
}



